import { Mat } from "./math";
import './style.css'

function Dom<T extends keyof HTMLElementTagNameMap>(type: T) {
  return document.createElement(type);
}
function Div() { return Dom('div'); }
function Flex(...es: (string | Node)[]) {
  const div = Div();
  div.className = 'flex';
  div.append(...es);
  return div;
}
function Hr() { return Dom('hr'); }
function Br() { return Dom('br'); }
function Text(s: string) {
  const pre = Dom('pre');
  pre.innerHTML = s;
  return pre;
}
function Button(text: string, callback: () => void) {
  const b = document.createElement('button');
  b.innerHTML = text;
  b.addEventListener('click', callback);
  return b;
}
function Input() {
  const t = document.createElement('textarea');
  t.rows = 4;
  return t;
}

function MatDiv(name: string, A: Mat): HTMLDivElement {
  const div = Div();
  div.append(
    `${name} `,
    Button('copy', () => navigator.clipboard.writeText(`${A}`)),
    Br(),
    Text(`${A}`),
  );
  return div;
}

class Manager {
  public readonly view = Div();
  public readonly cmd = Div();
  constructor(public readonly app: HTMLDivElement) {
    this.cmd.append(Button('input', () => this.addInput()));
    this.cmd.className = 'cmd';
    app.append(this.view, this.cmd, Br(), Br(), Br(), Br());
  }
  addInput() {
    const div = Div();
    {
      const At = Input();
      const Bt = Input();
      Bt.cols = 5;
      div.append(At, ' ', Bt, Br(),
        //--------------------------------------------------------
        Button('solve', () => {
          const As = At.value;
          const Bs = Bt.value;
          try {
            if (As.trim() === '')
              throw new Error('A is Empty');
            if (Bs.trim() === '')
              throw new Error('b is Empty');
            const A = Mat.fromString(As);
            const b = Mat.fromString(Bs).T().d[0];
            const { x, y } = Mat.solve(A, b);
            this.addOutput(
              MatDiv('x', new Mat([x])),
              Text('    '),
              MatDiv('y', new Mat([y])),
            );
          }
          catch (e) {
            this.addError(e);
          }
        }),
        //--------------------------------------------------------
        Button('LU', () => {
          try {
            const v = At.value;
            const A = Mat.fromString(v);
            const { L, U } = A.toLU();
            this.addOutput(Flex(
              MatDiv('L', L),
              MatDiv('U', U)));
          } catch (e) {
            this.addError(e);
          }
        }),
        //--------------------------------------------------------
        Button('tran B', () => {
          try {
            const B = Mat.fromString(Bt.value);
            Bt.value = `${B.T()}`;
          } catch (e) {
            this.addError(e);
          }
        }),
        //--------------------------------------------------------
        Button('remove', () => {
          if (Bt.value !== '')
            Bt.value = '';
          else if (At.value !== '')
            At.value = '';
          else
            div.remove();
        }), Hr());
    }
    this.view.appendChild(div);
  }
  addOutput(...eles: (string | Node)[]) {
    const div = Div();
    div.append(
      ...eles,
      Button('remove', () => div.remove()),
      Hr());
    div.className = 'output';
    this.view.appendChild(div);
  }
  addError(e: unknown) {
    const div = Div();
    div.style.background = '#faa';
    div.append(
      e instanceof Error ? e.message : `${e}`,
      Hr());
    div.addEventListener('click', () => div.remove());
    this.view.appendChild(div);
  }
}

const div = Div();
document.body.append(div);
(window as any).m = new Manager(div); 
